package org.testis.testdriver;

import org.testis.testdriver.conditions.ConditionEvaluatable;
import org.testis.data.TestStepDataObject;
import org.testis.testdriver.AtofEnumaration.TestPartType;
import org.testis.utils.UuidGenerator;
import com.mallardsoft.tuple.Pair;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

public abstract class TestPart implements ConditionEvaluatable {

    /**
     * name of the TestPart as String
     */
    protected String m_testPartName;
    /**
     * id of the TestPart as String
     */
    protected String m_testPartId;
    /**
     * description of the TestPart as String
     */
    protected String m_partDescription;
    /**
     * retry count when the TestPart fails as integer
     */
    protected int m_retryOnFailCount;
    /**
     * holds the reference to parent TestPart
     */
    protected TestPart m_parent;
    /**
     * holds the reference to child TestParts list.
     */
    protected LinkedList<TestPart> m_testPartList;
    /**
     * boolean attribute that holds if the TestPart is critical or not.
     */
    protected boolean m_isCritical = false;
   
    /**
     * boolean attribute that holds the direction of the equals algorithm.
     * We need this because we have to decide which direction the tree will
     * be validated.
     */
    protected boolean m_equalstopdown = false;
    /**
     * Holds the runtime unique guid for the Test Part.
     */
    protected String m_partGuid;
    /**
     * Holds the Test Part Type info of the TestPart
     */
    protected TestPartType m_containerType;
    /*
    Holds the number of retries applied to the test step;
     */
    protected int m_retryCount;
   
    protected String[] m_runOperations;

    /*
     * Holds the condition strings provided for the Test Part.
     */
    protected LinkedList<String> m_conditions;

    // Run Time Parameters

     /**
     * boolean attribute that holds the result of the TestPart.
     */
    protected boolean m_testPartResult = true;

     /**
     * Holds the result set specific to TestCase.
     */
    protected Map<Short, Pair<TestStepDataObject, Boolean>> m_case_resultSet;

     /**
     * Holds the List of Failed condition strings.
     */
    protected List<String> m_failedconditions;

   /**
     * Holds result postfix
     */
    private String m_storeResultPostfix;

    public TestPart() {

        this.m_partGuid = UuidGenerator.generateMsgId();
        m_case_resultSet =
                new HashMap<Short, Pair<TestStepDataObject, Boolean>>();
        m_conditions = new LinkedList<String>();

        m_failedconditions = new LinkedList<String>();
    }

    /**
     * This is the abstract test driving method. The derived classes will implement
     * this method for concrete test driving purposes
     * @param mode Test driving method will be declared with this parameter.      
     */
    public abstract boolean run(AtofEnumaration.DebuggingMode mode) throws
            InterruptedException;

    /**
     * This is the abstract TestPart adding method. The derived classes will implement
     * this method for adding TestPart to their list.
     * @param testPart TestPart that will be added.      
     */
    public abstract void add(TestPart testPart);

    /**
     * This is the abstract TestPart removing method. The derived classes will implement
     * this method for removing TestPart from their list.
     * @param testPart The TestPart that will be removed.      
     */
    public abstract void remove(TestPart testPart);

    /**
     * This is the abstract long name getting method.
     * Used when called from child
     * @return String returns the long name as string.
     */
    public abstract String getPartNameLongFromChild();

    /**
     * This is the abstract long guid getting method.
     * Used when called from child
     * @return String returns the long guid as string.
     */
    public abstract String getPartGuidLongFromChild();
     /**
     * This is the abstract long name getting method.
     * Used when called from containerItself
     * @return String returns the long name as string.
     */
    public abstract String getPartNameLongFromContainer();

    /**
     * This is the abstract long guid getting method.
     * Used when called from containerItself
     * @return String returns the long guid as string.
     */
    public abstract String getPartGuidLongFromContainer();

     /**
     * This is the abstract
     * Called when execution of a Test Design Tree is started.
     * @return String returns the long guid as string.
     */
    public abstract void resetTestPartRunTimeParameters();



    /**
     * This is the method used to get the parent testPart object. 
     */



    public TestPart getParent() {
        return m_parent;
    }

    /**
     * This is the method used to set the parent TestPart object. 
     * @param TestPart The parent TestPart object that will be set.
     */
    public void setParent(TestPart val) {
        this.m_parent = val;
    }

    /**
     * Get accessor to m_testPartResult
     */
    public boolean getTestPartResult() {
        return m_testPartResult;
    }

    /**
     * Set accessor to m_testPartResult
     */
    public void setTestPartResult(boolean testPartResult) {
        this.m_testPartResult = testPartResult;
    }

    public void andTestPartResult(boolean testPartResult) {
        this.m_testPartResult &= testPartResult;
    }

    /**
     * Get accessor to m_testPartList
     */
    public LinkedList<TestPart> getTestPartList() {
        return m_testPartList;
    }

    public abstract TestIterator createIter();

    /**
     * Get accessor to m_partDescription
     */
    public String getPartDescription() {
        return m_partDescription;
    }

    /**
     * Get accessor to m_testPartName
     */
    public String getPartName() {
        return "[" + m_containerType.name() + "] " + m_testPartName;
    }

    /**
     * Get accessor to m_testPartId
     */
    public String getPartId() {
        return m_testPartId;
    }

    /**
     * Get accessor to m_retryOnFailCount
     */
    public int getRetryOnFailCount() {
        return m_retryOnFailCount;
    }

    /**
     * Set accessor to m_retryOnFailCount
     */
    public void setRetryOnFailCount(int retryCount) {
        this.m_retryOnFailCount = retryCount;
    }

    /**
     * Get accessor to m_isCritical
     */
    public boolean getIsCritical() {
        return m_isCritical;
    }

    /**
     * Set accessor to m_isCritical
     */
    public void setIsCritical(boolean m_isCritical) {
        this.m_isCritical = m_isCritical;
    }

    /**
     * Get accessor to m_partGuid
     */
    public String getPartGuid() {
        return m_partGuid;
    }

    /**
     * Set accessor to m_partGuid
     */
    public void setPartGuid(String m_partGuid) {
        this.m_partGuid = m_partGuid;
    }

    public AtofEnumaration.TestPartType getContainerType() {
        return m_containerType;
    }

    public String[] getRunOperations() {
        return m_runOperations;
    }

    public void setRunOperations(String[] runOperations) {
        this.m_runOperations = runOperations;
    }

    public int getRetryCount() {
        return m_retryCount;
    }

    public void setRetryCount(int m_retryCount) {
        this.m_retryCount = m_retryCount;
    }

    public Map<Short, Pair<TestStepDataObject, Boolean>> getCaseResultSet() {
        return m_case_resultSet;
    }

    public LinkedList<String> getConditions() {
        return m_conditions;
    }

    public void setConditions(LinkedList<String> conditions) {
        this.m_conditions = conditions;
    }

    public List<String> getFailedConditions() {
        return m_failedconditions;
    }
    

    /**
     * This method is used to set the m_equalstopdown attribute of this class
     * and its child classes m_equalstopdown attributes. By setting this attribute 
     * we will choose in which direction the tree will be evaluated for equation
     * to another object of the same type by overriding the equals operator. If
     * we do not avoid bi-directional evaluation the tree will enter an infinite 
     * loop and fail with Stack Overflow exception.
     */
    public void setEqualsTopDown(boolean equalstopdown) {
        this.m_equalstopdown = equalstopdown;
        if (this instanceof StepContainer) {
            ListIterator ourIter = m_testPartList.listIterator();
            while (ourIter.hasNext()) {
                TestPart current = (TestPart) ourIter.next();
                current.setEqualsTopDown(equalstopdown);
            }
        }
    }

    @Override
    public int hashCode() {
        int hash = 6;
        hash = 59 * hash
                + (this.m_testPartName != null ? this.m_testPartName.hashCode() : 0);
        hash = 59 * hash
                + (this.m_testPartId != null ? this.m_testPartId.hashCode() : 0);
        hash = 59 * hash
                + (this.m_containerType != null ? this.m_containerType.hashCode()
                : 0);
        hash = 59 * hash
                + (this.m_partDescription != null
                ? this.m_partDescription.hashCode() : 0);
        hash =
                11 * hash
                + (this.m_case_resultSet != null ? this.m_case_resultSet.hashCode()
                : 0);
        hash = 59 * hash + this.m_retryOnFailCount;
        if (m_equalstopdown) {
            hash = 59 * hash
                    + (this.m_parent != null ? 1 : 0);
            hash =
                    59 * hash
                    + (this.m_testPartList != null ? this.m_testPartList.hashCode() : 0);
        } else {
            hash = 59 * hash
                    + (this.m_parent != null ? this.m_parent.hashCode() : 0);
            hash = 59 * hash
                    + (this.m_testPartList != null ? this.m_testPartList.size() : 0);
        }
        hash = 59 * hash + (this.m_isCritical ? 1 : 0);
        hash = 59 * hash + (this.m_testPartResult ? 1 : 0);
        hash = 59 * hash
                + (this.m_conditions != null ? this.m_conditions.size() : 0);
        return hash;
    }

    /**
     * @return the m_storeResultPostfix
     */
    public String getStoreResultPostfix() {
        return m_storeResultPostfix;
    }

    /**
     * @param m_storeResultPostfix the m_storeResultPostfix to set
     */
    public void setStoreResultPostfix(String storeResultPostfix) {
        this.m_storeResultPostfix = storeResultPostfix;
    }
}

